package work.sajor.wechatpush.util;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.URI;
import java.util.Map;


/**
 * User: x
 * Date: 2018/8/10
 * Time: 4:06 PM
 */
public class HttpConnector {

    private static Logger log = LoggerFactory.getLogger(HttpConnector.class);

    protected static final long KeepAliveDurationMillis = 5000;
    protected static final int ConnectionTimeoutMillis = 1000;
    protected static final int SocketTimeoutMillis = 5000;
    protected static final int MaxPerRoute = 30;
    protected static final int MaxTotal = 30;

    protected static CloseableHttpClient getHttpClient() {
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        cm.setDefaultMaxPerRoute(MaxPerRoute);
        cm.setMaxTotal(MaxTotal);

        return HttpClientBuilder.create()
                .setConnectionManager(cm)
                .setKeepAliveStrategy((response, context) -> KeepAliveDurationMillis)
                .setRetryHandler(new DefaultHttpRequestRetryHandler())
                .setDefaultRequestConfig(RequestConfig.custom()
                        .setConnectionRequestTimeout(ConnectionTimeoutMillis)
                        .setConnectTimeout(ConnectionTimeoutMillis)
                        .setSocketTimeout(SocketTimeoutMillis)
                        .build())
                .build();
    }

    public static String get(String url) {
        CloseableHttpClient httpClient = getHttpClient();
        try {
            HttpGet httpGet = new HttpGet(url);
            try {
                CloseableHttpResponse httpResponse = httpClient.execute(httpGet);
                if (httpResponse.getEntity() != null) {
                    return EntityUtils.toString(httpResponse.getEntity());
                }
            } catch (IOException e) {
                log.error("[HTTP GET] url={}, error={}", url, e);

            }
            return null;
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                log.error("[HTTP GET] {} close http client error. {}", url, e);
            }
        }
    }

    public static String getWithHeader(Map<String, String> headers, String url) {
        CloseableHttpClient httpClient = getHttpClient();
        try {
            HttpGet httpGet = new HttpGet(url);
            try {
                if (null != headers) {
                    headers.forEach((k, v) -> httpGet.addHeader(k, v));
                }
                CloseableHttpResponse httpResponse = httpClient.execute(httpGet);
                if (httpResponse.getEntity() != null) {
                    return EntityUtils.toString(httpResponse.getEntity());
                }
            } catch (IOException e) {
                log.error("[HTTP GET] url={}, error={}", url, e);

            }
            return null;
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                log.error("[HTTP GET] {} close http client error. {}", url, e);
            }
        }
    }

    public static String delete(String url, String jsonData) {
        CloseableHttpClient httpClient = getHttpClient();
        try {
            HttpDeleteWithBody httpDelete = new HttpDeleteWithBody(url);
            try {
                if (StringUtils.isNotBlank(jsonData)) {
                    httpDelete.setEntity(new StringEntity(jsonData, ContentType.APPLICATION_JSON));
                }
                CloseableHttpResponse httpResponse = httpClient.execute(httpDelete);
                if (httpResponse.getEntity() != null) {
                    return EntityUtils.toString(httpResponse.getEntity());
                }
            } catch (IOException e) {
                log.error("[HTTP DELETE] url={}, error={}", url, e);

            }
            return null;
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                log.error("[HTTP DELETE] {} close http client error. {}", url, e);
            }
        }
    }

    public static String delete(String url, String jsonData, Map<String, String> map) {
        CloseableHttpClient httpClient = getHttpClient();
        try {
            HttpDeleteWithBody httpDelete = new HttpDeleteWithBody(url);
            map.forEach((key, value) -> {
                httpDelete.setHeader(key, value);
            });
            try {
                if (StringUtils.isNotBlank(jsonData)) {
                    httpDelete.setEntity(new StringEntity(jsonData, ContentType.APPLICATION_JSON));
                }
                CloseableHttpResponse httpResponse = httpClient.execute(httpDelete);
                if (httpResponse.getEntity() != null) {
                    return EntityUtils.toString(httpResponse.getEntity());
                }
            } catch (IOException e) {
                log.error("[HTTP DELETE] url={}, error={}", url, e);

            }
            return null;
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                log.error("[HTTP DELETE] {} close http client error. {}", url, e);
            }
        }
    }

    public static String putJsonHeader(Map<String, String> headers, String url, String jsonData) {
        CloseableHttpClient httpClient = getHttpClient();
        try {
            HttpPut httpPut = new HttpPut(url);
            try {
                httpPut.setEntity(new StringEntity(jsonData, ContentType.APPLICATION_JSON));
                if (null != headers) {
                    headers.forEach((k, v) -> httpPut.addHeader(k, v));
                }
                CloseableHttpResponse httpResponse = httpClient.execute(httpPut);
                if (httpResponse.getEntity() != null) {
                    return EntityUtils.toString(httpResponse.getEntity());
                }
            } catch (IOException e) {
                log.error("[HTTP putJsonHeader] url={}, error={}", url, e);

            }
            return null;
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                log.error("[HTTP putJsonHeader] {} close http client error. {}", url, e);
            }
        }
    }

    public static String putJson(String url, String jsonData) {
        CloseableHttpClient httpClient = getHttpClient();
        try {
            HttpPut httpPut = new HttpPut(url);
            try {
                httpPut.setEntity(new StringEntity(jsonData, ContentType.APPLICATION_JSON));
                CloseableHttpResponse httpResponse = httpClient.execute(httpPut);
                if (httpResponse.getEntity() != null) {
                    return EntityUtils.toString(httpResponse.getEntity());
                }
            } catch (IOException e) {
                log.error("[HTTP PUT] url={}, error={}", url, e);

            }
            return null;
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                log.error("[HTTP PUT] {} close http client error. {}", url, e);
            }
        }
    }

    public static String postJsonHeader(Map<String, String> headers, String url, String jsonData) {
        CloseableHttpClient httpClient = getHttpClient();
        try {
            HttpPost httPost = new HttpPost(url);
            try {
                if (StringUtils.isNotEmpty(jsonData)) {
                    httPost.setEntity(new StringEntity(jsonData, ContentType.APPLICATION_JSON));
                }
                if (null != headers) {
                    headers.forEach((k, v) -> httPost.addHeader(k, v));
                }
                CloseableHttpResponse httpResponse = httpClient.execute(httPost);
                if (httpResponse.getEntity() != null) {
                    return EntityUtils.toString(httpResponse.getEntity());
                }
            } catch (Exception e) {
                log.error("[HTTP POST] url={}, error={}", url, e);
            }
            return null;
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                log.error("[HTTP POST] {} close http client error. {}", url, e);
            }
        }
    }

    public static String postJson(String url, String jsonData) {
        CloseableHttpClient httpClient = getHttpClient();
        try {
            HttpPost httPost = new HttpPost(url);
            try {
                httPost.setEntity(new StringEntity(jsonData, ContentType.APPLICATION_JSON));
                CloseableHttpResponse httpResponse = httpClient.execute(httPost);
                if (httpResponse.getEntity() != null) {
                    return EntityUtils.toString(httpResponse.getEntity());
                }
            } catch (Exception e) {
                log.error("[HTTP POST] url={}, error={}", url, e);

            }
            return null;
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                log.error("[HTTP POST] {} close http client error. {}", url, e);
            }
        }
    }

    static class HttpDeleteWithBody extends HttpEntityEnclosingRequestBase {
        private static final String METHOD_NAME = "DELETE";

        @SuppressWarnings("unused")
        public HttpDeleteWithBody() {
        }

        @SuppressWarnings("unused")
        public HttpDeleteWithBody(URI uri) {
            setURI(uri);
        }

        public HttpDeleteWithBody(String uri) {
            setURI(URI.create(uri));
        }

        @Override
        public String getMethod() {
            return METHOD_NAME;
        }
    }
}
